package org.jackysoft.service.impl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.jackysoft.entity.ArmDailyProductReporter;
import org.jackysoft.entity.ArmItem;
import org.jackysoft.entity.ArmMonthlyFinishedProductReporter;
import org.jackysoft.entity.DataItem;
import org.jackysoft.entity.Department;
import org.jackysoft.entity.DepartmentType;
import org.jackysoft.entity.ForwardStore;
import org.jackysoft.entity.Product;
import org.jackysoft.entity.ResearchArmItem;
import org.jackysoft.entity.User;
import org.jackysoft.report.ArmItemEqualPredicate;
import org.jackysoft.report.DataItemType;
import org.jackysoft.report.ReportConstant;
import org.jackysoft.report.ReporterLifecycle;
import org.jackysoft.report.visitor.ArmItemVisitor;
import org.jackysoft.service.AbstractReportServiceImpl;
import org.jackysoft.service.ArmDailyReporterService;
import org.jackysoft.service.ArmMonthlyFinishedReporterService;
import org.jackysoft.service.DailyResetDateService;
import org.jackysoft.service.DepartmentService;
import org.jackysoft.service.ForwardStoreService;
import org.jackysoft.util.SpringSecurityUtils;
import org.joda.time.DateTime;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;



@Service
@Transactional
public class ArmMonthlyFinishedReporterServiceImpl extends
		AbstractReportServiceImpl<ArmMonthlyFinishedProductReporter> implements
		ArmMonthlyFinishedReporterService {

	/**
	 * 
	 */
	private static final long serialVersionUID = 6453225434520607858L;
	private static final Log logger = LogFactory
			.getLog(ArmMonthlyFinishedReporterServiceImpl.class);
	protected Set<DataItemType> includeDataTypes = Sets.newHashSet();
	private DepartmentService deptService;
	private ArmDailyReporterService dailyService;
	private ForwardStoreService storeService;
	private DailyResetDateService resetDayService;

	public ArmMonthlyFinishedReporterServiceImpl() {
		this.type = ArmMonthlyFinishedProductReporter.class;
		includeDataTypes.add(DataItemType.ForwardStore);
		includeDataTypes.add(DataItemType.FinalStore);
		includeDataTypes.add(DataItemType.Waster);
		includeDataTypes.add(DataItemType.Test);

	}

	@Override
	public ArmMonthlyFinishedProductReporter get(String id)
			throws DataAccessException {
		if (id == null)
			return null;
		ArmMonthlyFinishedProductReporter t = super.get(id);
		pickRestArmItem(t);
		return t;
	}

	@Override
	public void create(ArmMonthlyFinishedProductReporter t)
			throws DataAccessException {
		if (!checkAuthority())
			return;
		beforeCreateAction(t, t.getDepartment());
		t.setReportLifecycle(ReporterLifecycle.MonthFinished);
		super.create(t);
		afterCreateAction(t, t.getDepartment());
	}

	/**
	 * <dd>生成原则,原则上本月最后一张日报表的数据就是 <dd>月报的最终数据了,而由于月报需要额外的数据项 <dd>
	 * 不能和日报同时上报,因此需要新建出来额外的几 <dd>项数据项来填充日报
	 * 
	 * @return 返回生成的本月报表,同时删除先前的本月报表
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * */
	@Override
	public Collection<ArmMonthlyFinishedProductReporter> generateAll()
			throws DataAccessException {
		if (!checkAuthority())
			return Collections.emptyList();
		Collection<Department> depts = deptService
				.getsByType(DepartmentType.Manufacture);
		Collection<ArmMonthlyFinishedProductReporter> rpts = Lists
				.newArrayList();

		DateTime date = DateTime.now();
		for (Department dept : depts) {
			ArmMonthlyFinishedProductReporter r = generate(date, dept);
			if (r != null)
				rpts.add(r);
		}
		return rpts;
	}

	/**
	 * <dd>生成原则,原则上本月最后一张日报表的数据就是 <dd>月报的最终数据了,而由于月报需要额外的数据项 <dd>
	 * 不能和日报同时上报,因此需要新建出来额外的几 <dd>项数据项来填充日报
	 * 
	 * @return 返回生成的本月报表,同时删除先前的本月报表
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * */
	@Override
	public Collection<ArmMonthlyFinishedProductReporter> generateAll(
			DateTime date) throws DataAccessException {
		if (!checkAuthority())
			return Collections.emptyList();
		Collection<Department> depts = deptService
				.getsByType(DepartmentType.Manufacture);
		Collection<ArmMonthlyFinishedProductReporter> rpts = Lists
				.newArrayList();

		for (Department dept : depts) {
			ArmMonthlyFinishedProductReporter r = generate(date, dept);
			if (r != null)
				rpts.add(r);
		}
		return rpts;
	}

	/**
	 * 统计本月的数据项
	 * 
	 * @param needForward
	 *            是否需要统计前期
	 * @param t
	 *            报表
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * */
	private void beforeCreateAction(ArmMonthlyFinishedProductReporter t,
			Department dept) {
		
		//统计年初和月初
		statForwardStore(t, dept);
		
		//统计月末在产
		statFinalStore(t, dept);
		
		//计算累计
		updateAccumulate(t, t.getLast());
		
		
		List<ArmItem> items = t.getArmItems();
		Collection<ArmItem> mitems = dataItemUtil.mergeArmItems(items);
		t.getArmItems().clear();
		t.getArmItems().addAll(mitems);

	}

	/**
	 * 统计期初结存,包括年初和月初,数据项需要向现有数据项新增加
	 * 
	 * @param t
	 *            数据项
	 * @param dept
	 *            部门
	 **/
	protected void statForwardStore(ArmMonthlyFinishedProductReporter t,
			Department dept) {
				
		DateTime cal = t.getCreateDate();

		int year = cal.getYear();

		// 通常是上月报表
		ArmMonthlyFinishedProductReporter thisYearLast = lastOfYear(year, dept);

		DateTime prevYear = cal.minusYears(1);

		int lyear = prevYear.getYear();

		// 去年最后一个月报表
		ArmMonthlyFinishedProductReporter prevYearLast = lastOfYear(lyear, dept);
		for (ArmItem ai : t.getArmItems()) {

			DataItem di = dataItemUtil.getDataItem(ai,
					DataItemType.ForwardStore);
			if (di == null) {
				di = new DataItem();
				ai.getDataItems().put(DataItemType.ForwardStore.toString(), di);
			}
			// 上月月末结存作为本月月初
			if (thisYearLast != null) {
				int month = thisYearLast.getCreateDate().getMonthOfYear();
				
				//上月该产品数据项
				ArmItem target = reportUtil.find(thisYearLast.getArmItems(),ai,ai.new TypedComparator());
				if (target != null) {
					di.setAccumulateValue(dataItemUtil.getDataItemAtom(target,DataItemType.FinalStore));
				} else {
					// 如果上月结存不存在,则在结存库里直接查询结存数
					ForwardStore fs = storeService.get(dept, year, month,
							ai.getHost(), ai.getItemType());
					if (fs != null) {
						di.setAccumulateValue(fs.getMonthValue());
					}

				}
			}
			// 上年最后一月结存作为本月年初
			if (prevYearLast != null) {
				int month = prevYearLast.getCreateDate().getMonthOfYear();
				ArmItem target = reportUtil.findByCode(
						prevYearLast.getArmItems(), ai);
				if (target != null) {
					di.setAtomValue(dataItemUtil.getDataItemAtom(target,
							DataItemType.FinalStore));
				} else {
					// 如果年结存不存在,则在结存库里直接查询结存数
					ForwardStore fs = storeService.get(dept, lyear, month,
							ai.getHost(), ai.getItemType());
					if (fs != null) {
						di.setAtomValue(fs.getYearValue());
					}
				}
			}
		}
	}

	/**
	 * 统计月末在产,此数据项是根据现有的数据项计算所得
	 * */
	private void statFinalStore(ArmMonthlyFinishedProductReporter t,
			Department dept) {
		for (ArmItem arm : t.getArmItems()) {
			if (arm == null)
				continue;
			// 年初
			Double forward = dataItemUtil.getDataItemAtom(arm,
					DataItemType.ForwardStore);

			// 当月动工累计
			Double local = dataItemUtil.getDataItemAccumulation(arm,
					DataItemType.Action);

			// 当月合格累计
			Double passed = dataItemUtil.getDataItemAccumulation(arm,
					DataItemType.Passed);

			// 当月废品累计
			Double waster = dataItemUtil.getDataItemAccumulation(arm,
					DataItemType.Waster);

			// 当月试销累计
			Double test = dataItemUtil.getDataItemAccumulation(arm,
					DataItemType.Test);

			// 期末结存数
			Double finalStore = forward + local - passed - waster - test;

			DataItem finalData = dataItemUtil.getDataItem(arm,
					DataItemType.FinalStore);

			if (finalData == null) {
				finalData = new DataItem();
				arm.getDataItems().put(DataItemType.FinalStore.toString(),
						finalData);
			}
			finalData.setAtomValue(finalStore);

		}

	}

	@Override
	public void update(ArmMonthlyFinishedProductReporter t)
			throws DataAccessException {
		if (!checkAuthority())
			return;

		ArmMonthlyFinishedProductReporter at = get(t.getId());
		ArmMonthlyFinishedProductReporter last = at.getLast();

		at.setCreateDate(t.getCreateDate());
		at.setDepartment(t.getDepartment());
		at.setMaker(t.getMaker());
		at.setLast(last);
		at.getArmItems().clear();
		at.getArmItems().addAll(t.getArmItems());
		beforeCreateAction(at, t.getDepartment());
		super.update(at);
	}

	@Resource(name = ReportConstant.MonthlyFinishedProductVisitor)
	public void setAccumulateVisitor(ArmItemVisitor accumulateVisitor) {
		super.setAccumulateVisitor(accumulateVisitor);
	}

	@Override
	public int clean(DateTime cal, Department dept) throws DataAccessException {

		int year = cal.getYear();
		int month = cal.getMonthOfYear();

		String hql = String
				.format("from  %s  r where year(r.createDate)=:year and month(r.createDate)=:month and r.department.id=:did",
						table());
		List list = query(hql).setInteger("year", year)
				.setInteger("month", month).setString("did", dept.getId())
				.list();
		super.remove(list);
		return 0;
	}

	@Override
	public ArmMonthlyFinishedProductReporter getLastOfYear(int year)
			throws DataAccessException {

		return null;
	}

	@Resource
	public void setDeptService(DepartmentService deptService) {
		this.deptService = deptService;
	}

	@Resource
	public void setDailyService(ArmDailyReporterService ds) {
		this.dailyService = ds;
	}

	@Override
	public ArmMonthlyFinishedProductReporter generate(DateTime theDate,
			Department dept) throws DataAccessException {
		clean(theDate, dept);

		logger.info("start generate monthly report for "+dept.getName()+","+dept.getId());
		// 查找该部门指定日期的最后一张日报
		final ArmDailyProductReporter finalReport = dailyService.getByTopDate(
				theDate, dept);

		if (finalReport == null) {
			logger.warn("没有找到 [" + dept.getName() + "] "
					+ theDate.toString("yyyy-MM-dd") + "的日报,生成已经终止");
			return null;
		}

		// 查找该部门指定日期的月报
		ArmMonthlyFinishedProductReporter target = get(theDate, dept);
		if (target == null)
			target = new ArmMonthlyFinishedProductReporter();
		String lastId = getLast(dept);
		// 查找本部门最近一次月报
		ArmMonthlyFinishedProductReporter lastRpt = get(lastId);
		User user = SpringSecurityUtils.getCurrentUser();
		target.setMaker(user);
		target.setDepartment(dept);
		target.setLast(lastRpt);
		target.setCreateDate(theDate);
		target.setUpdateDate(theDate);
		target.setReportLifecycle(ReporterLifecycle.MonthFinished);
			
		for (ArmItem ai : finalReport.getArmItems()) {
			ArmItem cai = ai.clone();
			fillLocalArmitem(cai);
			cai.setId(null);
			target.getArmItems().add(cai);
		}
		
		for(ArmItem arm:finalReport.getResearchItems()){
			fillLocalArmitem(arm);			
			ResearchArmItem ra = new ResearchArmItem(
					arm.getHost().getCode(),
					arm.getHost().getName());
			ra.setRemark(arm.getRemark());
			ra.setDataItems(arm.getDataItems());
			target.getResearchItems().add(ra);
		}		
		
		// 填充生成的数据		
		create(target);		
		logger.info("monthly report generated for " + dept.getName()+"["+dept.getId()+"]@"+target.getCreateDate().toString("yyyy-MM-dd"));
		return target;
	}

	
	/**
	 * 创建完成月报后更新结存
	 * */
	private void afterCreateAction(ArmMonthlyFinishedProductReporter target,
			Department dept) {

		List<ArmItem> items = target.getArmItems();
		Collection<ForwardStore> fs = Lists.newArrayList();
		DateTime cdate = target.getCreateDate();
		for (ArmItem item : items) {
			ForwardStore f = new ForwardStore();
			f.setDepartment(dept);
			f.setYear(cdate.getYear());
			f.setMonth(cdate.getMonthOfYear());
			f.setYearValue(dataItemUtil.getDataItemAtom(item,
					DataItemType.ForwardStore));
			f.setMonthValue(dataItemUtil.getDataItemAtom(item,
					DataItemType.FinalStore));
			f.setOwner(item.getHost());
			f.setItemType(item.getItemType());
			fs.add(f);
		}

		storeService.clear(dept, cdate.getYear(), cdate.getMonthOfYear());
		storeService.create(fs);

	}

	/**
	 * 
	 * 填充当月数
	 * @param ArmItem ai
	 * 
	 * */
	void fillLocalArmitem(ArmItem ai){
		if(ai==null) return ;
		Map<String, DataItem> ditems  =  ai.getDataItems();
		if(ditems==null || ditems.isEmpty())return;
		Collection<DataItem> datas = ditems.values();
	    for(DataItem d:datas){
	    	d.setAtomValue(d.getAccumulateValue());
	    }
	}
	
	
	/**
	 * 把上月动工，本月未动工的数据项加进本月数据
	 * @param 当月月报
	 * 
	 **/
	protected void pickRestArmItem(ArmMonthlyFinishedProductReporter rpt) {
		ArmMonthlyFinishedProductReporter last = rpt.getLast();
		if (last == null) {
			logger.warn("月报表" + rpt + "没有前驱报表");
			return;
		}
		Collection<ArmItem> myItems = rpt.getArmItems();

		for (ArmItem lai : last.getArmItems()) {
			// 从本月报表里查找上月数据项
			ArmItem cai = reportUtil.find(myItems, lai);
			if (cai == null) {
				rpt.getLastItems().add(lai.clone());
			}
			// 统计前驱已经累计过了
		}

	}

	@Override
	public void remove(ArmMonthlyFinishedProductReporter t)
			throws DataAccessException {
		if (!checkAuthority())
			return;
		super.remove(t);
	}

	@Override
	public ArmMonthlyFinishedProductReporter get(int year, int month,
			Department dept) throws DataAccessException {
		if (dept == null)
			return null;
		String hql = String
				.format("from %s r where r.department.id='%s' and year(r.createDate)=%d and month(r.createDate)=%d",
						table(), dept.getId(), year, month);
		Iterator itr = getSession().createQuery(hql).iterate();
		return (ArmMonthlyFinishedProductReporter) (itr.hasNext() ? itr.next()
				: null);
	}

	Collection<ArmMonthlyFinishedProductReporter> getsAll(int year, int month) {
		String hql = String
				.format("from %s r where year(r.createDate)=%d and month(r.createDate)=%d",
						table(), year, month);
		List list = getSession().createQuery(hql).list();
		return list;
	}

	@Override
	public byte[] export2Xls(int year, int month) throws DataAccessException,
			IOException {
		Workbook wb = new HSSFWorkbook();
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		try {

			Collection<ArmMonthlyFinishedProductReporter> reports = getsAll(
					year, month);
			String[] title = { "代码", "产品名称", "单位", "年初", "月初", "本月动工", "动工累计",
					"本月合格", "合格累计", "本月废品", "废品累计", "本月试销", "试销累计", "结存", "备注" };
			DataItemType[] requiredDataTypes = { DataItemType.ForwardStore,
					DataItemType.Action, DataItemType.Passed,
					DataItemType.Waster, DataItemType.Test };
			for (ArmMonthlyFinishedProductReporter report : reports) {
				Department dept = report.getDepartment();
				String sheetName = dept.getName();

				Sheet sheet = wb.createSheet(sheetName);
				sheet.setColumnWidth(0, 20 * 256);
				sheet.setColumnWidth(1, 20 * 256);
				Row firstRow = sheet.createRow(0);
				// 添加表头
				for (int i = 0; i < title.length; i++) {

					Cell cell = firstRow.createCell(i);
					cell.setCellValue(title[i]);

				}
				Collection<ArmItem> aims = report.getArmItems();
				int row = 1;

				for (ArmItem ai : aims) {
					Product pdt = ai.getHost();

					Row theRow = sheet.createRow(row);

					int cindex = 0;

					// 代码
					theRow.createCell(cindex++).setCellValue(pdt.getCode());

					// 名称
					theRow.createCell(cindex++).setCellValue(pdt.getName());

					// 单位
					theRow.createCell(cindex++)
							.setCellValue(pdt.getPriceUnit());
					// 添加数据列

					for (DataItemType dtype : requiredDataTypes) {
						Cell dcell = theRow.createCell(cindex++);
						Double d = dataItemUtil.getDataItemAtom(ai, dtype);
						dcell.setCellValue(d);

						d = dataItemUtil.getDataItemAccumulation(ai, dtype);
						dcell = theRow.createCell(cindex++);
						dcell.setCellValue(d);
					}

					// 期末结存
					Double finalstore = dataItemUtil.getDataItemAtom(ai,
							DataItemType.FinalStore);
					theRow.createCell(cindex++).setCellValue(finalstore);

					// 添加备注列
					theRow.createCell(cindex++).setCellValue(ai.getRemark());
					sheet.setColumnWidth(cindex - 1, 30 * 256);
					row++;
				}

			}
		} finally {
			wb.write(os);

		}

		return os.toByteArray();
	}

	@Resource
	public void setStoreService(ForwardStoreService storeService) {
		this.storeService = storeService;
	}

	@Resource
	public void setResetDayService(DailyResetDateService resetDayService) {
		this.resetDayService = resetDayService;
	}
	
	@Override
	@Resource(name=ReportConstant.ArmItemRegionEqualPredicate)
	public void setArmItemEqualPredicate(
			ArmItemEqualPredicate armItemEqualPredicate) {
		super.setArmItemEqualPredicate(armItemEqualPredicate);
	
	}
}